/***************************************************
File:           LPK_ObjectDisplay.cs
Authors:        Christopher Onorati
Last Updated:   5/21/2019
Last Version:   2018.3.14

Description:
  This component serves as a display of values that is
  indicated by spawning and destroying objects.

This script is a basic and generic implementation of its 
functionality. It is designed for educational purposes and 
aimed at helping beginners.

Copyright 2018-2019, DigiPen Institute of Technology
***************************************************/

using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

namespace LPK
{

/**
* CLASS NAME  : LPK_ObjectDisplay
* DESCRIPTION : Create a display using objects (like hearts, arrows, etc.)
**/
[RequireComponent(typeof(Transform))]
public class LPK_ObjectDisplay : LPK_DisplayObject
{
    /************************************************************************************/

    [Tooltip("What object to serve as a display.")]
    [Rename("Display Prefab")]
    public GameObject m_pObjectPrefab;

    public Vector3 m_vecSpawnOffset;

    public int m_iMaxDisplayObjects = 5;

    public bool m_bDrawInitialDisplay;
    public int m_iInitialDisplayCount = 3;

    public bool m_bCopyDisplayerRotation;

    /************************************************************************************/

    //Previous value of the counter from last call.
    int m_iPreviousValue = 0;

    List<GameObject> m_pDisplayObjects = new List<GameObject>();

    /************************************************************************************/

    Transform m_cTransform;

    /**
    * FUNCTION NAME: OnStart
    * DESCRIPTION  : Initializes events.
    * INPUTS       : None
    * OUTPUTS      : None
    **/
    override protected void OnStart()
    {
        m_cTransform = GetComponent<Transform>();

        if (m_bDrawInitialDisplay)
            CreateInitialDisplay();

        m_iPreviousValue = m_iInitialDisplayCount;
    }

    /**
    * FUNCTION NAME: UpdateDisplay
    * DESCRIPTION  : Changes the count of objects to use in the display.
    * INPUTS       : _currentVal - Current value of the display.
    *                _maxVal     - Max value of the display.
    * OUTPUTS      : None
    **/
    override public void UpdateDisplay(float _currentVal, float _maxVal)
    {
        if (m_bPrintDebug)
            LPK_PrintDebug(this, "Display Update");

        //If the new value is greater than the older, create new display objects
        CreateObjects((int)_currentVal);

        //if the new value is smaller, then destroy the difference
        RemoveObjects((int)_currentVal);
    
        //Update old value to match the new one
        m_iPreviousValue = (int)_currentVal;
    }

    /**
    * FUNCTION NAME: CreateObjects
    * DESCRIPTION  : Creates objects used in the display.
    * INPUTS       : _createCount - How many objects to create in this pass.
    * OUTPUTS      : None
    **/
    void CreateObjects(int _createCount)
    {
        //If the new value is greater than the older, create new display objects
        if (_createCount > m_iPreviousValue)
        {
            for (int i = 0; i < _createCount - m_iPreviousValue && m_pDisplayObjects.Count < m_iMaxDisplayObjects; i++)
            {
                GameObject obj;

                if(!m_bCopyDisplayerRotation)
                    obj = Instantiate(m_pObjectPrefab, m_cTransform.position + (m_vecSpawnOffset * m_pDisplayObjects.Count), Quaternion.identity);
                else
                    obj = Instantiate(m_pObjectPrefab, m_cTransform.position + (m_vecSpawnOffset * m_pDisplayObjects.Count), m_cTransform.rotation);

                obj.transform.SetParent(transform);
                m_pDisplayObjects.Add(obj);
            }
        }
    }

    /**
    * FUNCTION NAME: OnEvent
    * DESCRIPTION  : Create the initial 
    * INPUTS       : None
    * OUTPUTS      : None
    **/
    void CreateInitialDisplay()
    {
        for(int i = 0; i < m_iInitialDisplayCount; i++)
        {
            GameObject obj;

            if(!m_bCopyDisplayerRotation)
                obj = Instantiate(m_pObjectPrefab, m_cTransform.position + (m_vecSpawnOffset * i), Quaternion.identity);
            else
                obj = Instantiate(m_pObjectPrefab, m_cTransform.position + (m_vecSpawnOffset * i), m_cTransform.rotation);

            obj.transform.SetParent(m_cTransform);
            m_pDisplayObjects.Add(obj);
        }
    }

    /**
    * FUNCTION NAME: RemoveObjects
    * DESCRIPTION  : Removes objects used in the display.
    * INPUTS       : _removeCount - How many objects to remove in this pass.
    * OUTPUTS      : None
    **/
    void RemoveObjects(int _removeCount)
    {
        if (_removeCount < m_iPreviousValue && m_pDisplayObjects.Count > 0)
        {
            int totalLosses = Mathf.Abs(_removeCount - m_iPreviousValue);

            for (int i = 0; i < totalLosses; i++)
            {
                GameObject obj = m_pDisplayObjects[m_pDisplayObjects.Count - 1];
                Object.Destroy(obj);
                m_pDisplayObjects.RemoveAt(m_pDisplayObjects.Count - 1);
            }
        }
    }
}

#if UNITY_EDITOR

[CustomEditor(typeof(LPK_ObjectDisplay))]
public class LPK_ObjectDisplayEditor : Editor
{
    SerializedProperty objectPrefab;

    /**
    * FUNCTION NAME: OnEnable
    * DESCRIPTION  : Save out serialized classes.
    * INPUTS       : None
    * OUTPUTS      : None
    **/
    void OnEnable()
    {
        objectPrefab = serializedObject.FindProperty("m_pObjectPrefab");
    }

    /**
    * FUNCTION NAME: OnInspectorGUI
    * DESCRIPTION  : Override GUI for inspector.
    * INPUTS       : None
    * OUTPUTS      : None
    **/
    public override void OnInspectorGUI()
    {
        LPK_ObjectDisplay owner = (LPK_ObjectDisplay)target;

        LPK_ObjectDisplay editorOwner = owner.GetComponent<LPK_ObjectDisplay>();

        EditorGUI.BeginDisabledGroup(true);
        EditorGUILayout.BeginHorizontal();
        EditorGUILayout.PrefixLabel("Script");
        editorOwner = (LPK_ObjectDisplay)EditorGUILayout.ObjectField(editorOwner, typeof(LPK_ObjectDisplay), false);
        GUILayout.EndHorizontal();
        EditorGUI.EndDisabledGroup();

        //Undo saving.
        Undo.RecordObject(owner, "Property changes on LPK_ObjectDisplay");

        //Component properties.
        GUILayout.Space(10);
        EditorGUILayout.LabelField("Component Properties", EditorStyles.boldLabel);

        EditorGUILayout.PropertyField(objectPrefab, true);
        owner.m_vecSpawnOffset = EditorGUILayout.Vector3Field(new GUIContent("Display Object", "Where to spawn the next object relative to the previous."), owner.m_vecSpawnOffset);
        owner.m_iMaxDisplayObjects = EditorGUILayout.IntField(new GUIContent("Max Display Objects", "Maximum amount of objects to show in the display."), owner.m_iMaxDisplayObjects);
        owner.m_bDrawInitialDisplay = EditorGUILayout.Toggle(new GUIContent("Draw Initial Objects", "Set to have objects created by default."), owner.m_bDrawInitialDisplay);

        if(owner.m_bDrawInitialDisplay)
            owner.m_iInitialDisplayCount = EditorGUILayout.IntField(new GUIContent("Initial Display Objects", "How many objects to display by default."), owner.m_iInitialDisplayCount);

        owner.m_bCopyDisplayerRotation = EditorGUILayout.Toggle(new GUIContent("Copy Displayer Rotation", "Copy the rotation of this object for spawning display objects."), owner.m_bCopyDisplayerRotation);

        //Debug properties.
        GUILayout.Space(10);
        EditorGUILayout.LabelField("Debug Properties", EditorStyles.boldLabel);

        owner.m_bPrintDebug = EditorGUILayout.Toggle(new GUIContent("Print Debug Info", "Toggle console debug messages."), owner.m_bPrintDebug);
        owner.m_sLabel = EditorGUILayout.TextField(new GUIContent("Label", "Notes for the user about this component.  This does nothing to the game or build."), owner.m_sLabel);

        //Apply changes.
        serializedObject.ApplyModifiedProperties();
    }
}

#endif  //UNITY_EDITOR

}   //LPK
